<?php
namespace Orx\User;
/**
 * User MailBox Object
 * To store mailbox connection params
 * 
 * @author Xavier VILAIN
 * @package ressource
 * @property int 		$idusr		User Id
 * @property string 	$email		Email
 * @property string 	$host		Host Name
 * @property int	 	$port		Port
 * @property int	 	$password	Password
 * @property datetime	$updated	Last Update
 */
use Fox\Dao\SqlFragment;
use Orx\Mailer\MailBox\Message\Synchronization;
class MailBox extends \Fox\Object {
	
	/**
	 * Unique IMAP Identifier
	 * @var string
	 */
	const IMAP = 'imap';
	
	/**
	 * Unique POP 3 Identifier
	 * @var string
	 */
	const POP3 = 'pop';
		
	/**
	 * Status Sync Success
	 * @var int
	 */
	const STATUS_SUCCESS = 1;

	/**
	 * New Email
	 * @var int
	 */
	const STATUS_NEW = 1;
	
	/**
	 * Status Sync in Error
	 * @var int
	 */
	const STATUS_ERROR = 9;

	/**
	 * Link to messages
	 */
	const LINK_MESSAGES = 'messages';
	
	/**
	 * Retreive last synchronization
	 * @var string
	 */
	const LINK_LAST_SYNCHRONIZATION = 'lastSynchronization';
		
	/**
	 * Retreive shared Users
	 * @var string
	 */
	const LINK_SHARED_USERS = 'sharedUsers';
			
	/**
	 * Retreive shared Groups
	 * @var string
	 */
	const LINK_SHARED_GROUPS = 'sharedGroups';
	
	/**
	 * Retreive all shared items
	 * @var string
	 */
	const LINK_SHARED_ITEMS = 'sharedItems';
	
	/**
	 * Tell the system that after loading this is a shared mailbox
	 * @var bool
	 */
	public $shared = false;
	
	/**
	 * Detect Host,Port and Username from email
	 * @return array
	 */
	public static function detectParams($email,$ssl = false) {
		$parts = explode('@',$email);
		$domain = $parts[count($parts)-1];
		if(preg_match('/(google|gmail)/',$domain)) {
			// Gmail Default
			$params['usessl'] = true;
			$params['domain'] ='gmail.com';
			$params['imap'] = 'imap.gmail.com';
			$params['imap_port'] = 993;
			$params['smtp'] = 'smtp.gmail.com';
			$params['smtp_port'] = 465;
			$params['pop3'] = 'pop.gmail.com';
			$params['pop3_port'] = 995;
			
			$params['username'] = $parts[0];
		} elseif(preg_match('/(yahoo)/',$domain)) {
			// Yahoo Default
			$params['domain'] ='yahoo.com';
			$params['usessl'] = true;
			$params['hostname'] ='yahoo.com';
			$params['imap'] = 'imap.mail.yahoo.com';
			$params['imap_port'] = 993;
			$params['smtp'] = 'smtp.mail.yahoo.com';
			$params['smtp_port'] = 465;
			$params['username'] = $parts[0];
		} else {
			$params['domain'] = $domain;
			$params['imap'] = 'imap.'.$domain;
			$params['imap_port'] = (!$ssl) ? 143 : 993;
			$params['smtp'] = 'smtp.'.$domain;
			$params['smtp_port'] = (!$ssl) ? 110 : 465;
			$params['pop3'] = 'pop3.'.$domain;
			$params['pop3_port'] = (!$ssl) ? 110 : 995;
			$params['username'] = $parts[0];
		}
		return $params;
	}
	
	/**
	 * 
	 * Find and return author email and full name if founded
	 * @param string $author
	 * @return string
	 */
	public static function parseAuthors($author) {
		$data = array();
		$author = str_replace('"',' ',$author);
		if(preg_match_all('/\<([^\)]*)\>/',$author,$matches)) {
			$data['email'] = strtolower($matches[1][0]);
			$from_name = trim(str_replace($matches[0][0],'',$author));
			if($from_name != $data['email']) {
				$data['name'] = $from_name;
			}
			
		} else {
			$data['email'] = strtolower(trim($author));
		}
		return $data;
	}
	
	/**
	 * 
	 * Find and return charset from content type string
	 * @param string $contentType
	 * @return string
	 */
	public static function parseCharset($contentType) {
		preg_match('/charset="(.+)"$/', $contentType, $matches);
		if(!isset($matches[1])) {
			preg_match('/charset=(.+)$/',$contentType, $matches);
		}
		if(isset($matches[1])) {
			$parts = explode(';',$matches[1]);
			$matches[1] = $parts[0];
		}
		if(!isset($matches[1])) {
			return null;
		} else {
			return strtolower($matches[1]);
		}
	}
	
	/**
	 * 
	 * Find and return filename from content type string
	 * @param string $contentType
	 * @return string
	 */
	public static function parseFilename($contentType) {
		preg_match('/filename="(.+)"$/', $contentType, $matches);
		if(!isset($matches[1])) {
			preg_match('/filename=(.+)$/',$contentType, $matches);
		}
		if(!isset($matches[1])) {
			preg_match('/name="(.+)"$/',$contentType, $matches);
		}
		if(!isset($matches[1])) {
			preg_match('/name=(.+)$/',$contentType, $matches);
		}
		if(isset($matches[1])) {
			$parts = explode(';',$matches[1]);
			$matches[1] = $parts[0];
		}
		if(!isset($matches[1])) {
			return null;
		} else {
			$matches[1] = str_replace('"','',$matches[1]);
			return trim($matches[1]);
		}
	}
	
	/**
	 * Register and Connect to the POP3 Server
	 * @return \Orx\Mailer\MailBox\Imap
	 */
	public function imap() {
		$uniquename = \User::CUSTOM_MAILBOX.$this->boxid.self::IMAP;
		if(!\Orx\Mailer\MailBoxManager::instance()->isRegistered($uniquename)) {
			// Test IMAP Connection
			\Orx\Mailer\MailBoxManager::instance()->register(array($uniquename => array(
				\Orx\Mailer\MailBoxManager::PARAM_HOST => $this->imap,
				\Orx\Mailer\MailBoxManager::PARAM_PORT => $this->imap_port,
				\Orx\Mailer\MailBoxManager::PARAM_USER => $this->email,
				\Orx\Mailer\MailBoxManager::PARAM_PASSWORD => $this->password(),
				\Orx\Mailer\MailBoxManager::PARAM_SSL => $this->usessl,
				\Orx\Mailer\MailBoxManager::PARAM_STORAGE_CLASS => \Orx\Mailer\MailBox\Imap::className()
			)));
		}
		return \Orx\Mailer\MailBoxManager::instance()->box($uniquename);
	}
	
	/**
	 * Get SMTP Transport Object
	 * 
	 * @return \Zend_Mail_Transport_Smtp
	 */
	public function smtp() {
		$smtpCfg['port'] = $this->smtp_port;
		switch($this->smtp_connection_mode) {
			case 'tls':
				$smtpCfg['ssl'] = 'tls';
				break;
			case 'ssl':
				$smtpCfg['ssl'] = 'ssl';
				break;
			default:
				break;
		}
		$smtpCfg['username'] = $this->username();
		$smtpCfg['auth'] = 'login';
		$smtpCfg['password'] = $this->password();
		$smtp = true;//Case of no SMTP setup

		// Set default mail transport
		if($smtp){
			$tr = new \Zend_Mail_Transport_Smtp($this->smtp,$smtpCfg);
		} else {
			$tr = new \Zend_Mail_Transport_Sendmail($smtpCfg);
		}
		/*
		\Zend_Mail::setDefaultTransport($tr);
		\Zend_Mail::setDefaultFrom($this->email,$this->name);
		*/
		return $tr;
	}
	
	/**
	 * Username
	 * @return string
	 */
	public function username() {
		$username = $this->username;
		if(empty($username)) {
			$username = $this->email;
		}
		$username = $this->email;
		return $username;
	}
	
	/**
	 * Register and Connect to the POP3 Server
	 * @return \Orx\Mailer\MailBox\Pop3
	 */
	public function pop3() {
		$uniquename = \User::CUSTOM_MAILBOX.$this->boxid.self::POP3;
		if(!\Orx\Mailer\MailBoxManager::instance()->isRegistered($uniquename)) {
			// Test IMAP Connection
			\Orx\Mailer\MailBoxManager::instance()->register(array($uniquename => array(
				\Orx\Mailer\MailBoxManager::PARAM_HOST => $this->pop3,
				\Orx\Mailer\MailBoxManager::PARAM_PORT => $this->pop3_port,
				\Orx\Mailer\MailBoxManager::PARAM_USER => $this->email,
				\Orx\Mailer\MailBoxManager::PARAM_PASSWORD => $this->password(),
				\Orx\Mailer\MailBoxManager::PARAM_SSL => $this->usessl,
				\Orx\Mailer\MailBoxManager::PARAM_STORAGE_CLASS => \Orx\Mailer\MailBox\Pop3::className()
			)));
		}
		return \Orx\Mailer\MailBoxManager::instance()->box($uniquename);
	}
	
	/**
	 * Decrypt Password from the DB
	 * 
	 */
	public function password() {
		return \Fox\Utilities::crypt($this->password,_APPLICATION_PRIVATE_KEY_);
	}
	
	/**
	 * Load only new Email from server according to last check date
	 * @
	 */
	public function synchronize($forced = false) {
		$newMessages = array();
		$now = time();
		try { 
			$query = \Fox\Dao::infos(\Orx\Mailer\MailBox\Message\Synchronization::className())
						->handle()->createQueryBuilder();
			$query->select('MAX(sync_date)')
					->from('message_synchronizations','t');
			$fragment = new SqlFragment('idusr = ? AND boxid = ? AND status = ? AND message_count > 0',array($this->idusr,$this->boxid,self::STATUS_SUCCESS));
			$lastImportDate = $fragment->pushIn($query)->execute()->fetchColumn();
			if(empty($lastImportDate)) {
				$lastImportDate = new \DateTime();
				$minus10Days = new \DateInterval('P10D');
				$lastImportDate = $lastImportDate->sub($minus10Days);
			} else {
				$lastImportDate = new \DateTime($lastImportDate);
			}
			// Delay Between last call and now 
			$actualDate = new \DateTime();
			$interval = $actualDate->diff($lastImportDate);

			$box = $this->box();
			$nbMessages = $box->count();
			
		} catch(\Exception $e) {
			
			return $this->log($now,
								self::STATUS_ERROR,
								Msg(\Hive::BUNDLE,\Hive::ERROR_CONNECTION_MAILBOX_FAILDED),
								print_r($e,true)
								);
		}
		if($nbMessages > 0) {
			try {
				$mbox = $box->handle();
				
				for ($i = $nbMessages; $i >= 0; $i--)
			    {
			    	$data = array();
			    	// 1. Extract Email Unique Id
			    	try {
						$message = $box->handle()->getMessage($i);
			    	 } catch(\Exception $e) {
			    	 	$j = 0;
			    		foreach($mbox as $currentMessage) {
							$j++;
							if($j == $i) {
								$message = $currentMessage;
								break;
							}
						}
			  	    }
			  	    if(empty($message)) {
			  	    	continue;
			  	    }
			    	try {
			  	    	$uniqueIds = $box->handle()->getUniqueId($i);
			  	    	if(is_array($uniqueIds) && isset($uniqueIds[$i])) {
			  	    		$data['uniqueid'] = $uniqueIds[$i];
			  	    	} elseif(!is_array($uniqueIds)) {
			  	    		$data['uniqueid'] = $uniqueIds;
			  	    	} else {
			  	    		$data['uniqueid'] = $i;
			  	    	}
			  	    } catch(\Exception $e) {
			  	    	$data['uniqueid'] = $i;
			  	    }
			    	if($message->headerExists('message-id')) {
						$data['externalid'] = $message->getHeader('message-id');
					} else {
						$data['externalid'] = $data['uniqueid'];
					}
					
			    	if(!\Orx\Mailer\MailBox\Message::isUnique($data['externalid'],
			    		$this->idusr,$this->boxid)) {
						$i = 0;
						break;
					}
					$date = new \DateTime(date("Y-m-d H:i:s",strtotime($message->date)));
					
					/*
					if($date <= $lastImportDate) {
						$i = 0;
						break;
					}
					*/
					$data['received_date'] = \Fox\Object::sqlDateTime($date);
					
					if($message->headerExists('thread-index')) {
						$data['thread_index'] = $message->getHeader('thread-index');
					}
					// Check if the message has been already imported ?
					
					// 2. Extract From Name & Email
					$data['fullfrom'] = utf8_encode($message->from);
					$data = array_merge($data,self::parseAuthors($data['fullfrom']));
					// 3. Extract To
					$receivers = explode(',',utf8_encode($message->to));
					foreach($receivers as $receiver) {
						$data['to'][] = self::parseAuthors($receiver);			
					}
			   		// 4. Extract Ccs
					if($message->headerExists('cc')) {
						$ccs = explode(',',utf8_encode($message->cc));
						foreach($ccs as $cc) {
							$data['cc'][] = self::parseAuthors($cc);			
						}
					}
					// 5. Subject
					if( mb_detect_encoding($message->subject) != 'UTF-8') {
						$data['subject'] = utf8_encode($message->subject);	
					} else {
						$data['subject'] = utf8_encode($message->subject);	
					}
					// 6. Is already been read ?
					if($message->hasFlag(\Zend_Mail_Storage::FLAG_SEEN)) {
						// Already Seen What should we do ?
					}
					
					// 7. Get Plain Text Content
					if($message->isMultipart()) {
						foreach (new \RecursiveIteratorIterator($message) as $part) {
							$headers = $part->getHeaders();
							$contentTransferEncoding = !empty($headers['content-transfer-encoding']) ? $headers['content-transfer-encoding'] : null;
							if($part->headerExists('content-disposition') ) {
								$contentDispositionType = strtok($part->{'content-disposition'},';');
							
								if($contentDispositionType == \Zend_Mime::DISPOSITION_ATTACHMENT || 
               			 			$contentDispositionType == \Zend_Mime::DISPOSITION_INLINE) {
               			 			// Its an attachement 
               			 			$data['attachements'][] = array('encoding' => $part->contentTransferEncoding,
               			 											'charset' => self::parseCharset($part->contentType),
               			 											'filename' => self::parseFilename($part->{'content-disposition'}),
               			 											'content' => $part->getContent(),
               			 										);

               			 		}
               			 		continue;
							}
							
							if (strtok($part->contentType, ';') == 'text/plain') {
								if(empty($data['content'])) {
									$data['content'] = null;
								}
								
								if($contentTransferEncoding == 'quoted-printable') {
									$content = trim(quoted_printable_decode($part->getContent()));
								} elseif($contentTransferEncoding == 'base64')  {
									// There is a file or an image inside the cotnent
									$content = base64_decode($part->getContent());
								} else {
									$content = $part->getContent();
								}
								
								//find the charset
								$charset = self::parseCharset($part->contentType);
								if($charset != 'utf-8') {
									$content = utf8_encode($content);
								}
								$content = trim($content);
								if(empty($content)) {
									continue;
								}
								$data['content'] .= $content;
							}
							if(strtok($part->contentType, ';') == 'text/html') {
								if(empty($data['content_html'])) {
									$data['content_html'] = null;
								}
								if($contentTransferEncoding == 'quoted-printable') {
									$data['content_html'] .= trim(quoted_printable_decode($part->getContent()));
								} elseif($contentTransferEncoding == 'base64') {
									$data['content_html'] .= base64_decode($part->getContent());
								} else {
									$data['content_html'] .= $part->getContent();
								}
							}
						}
					} else {
						$headers = $message->getHeaders();
						$contentTransferEncoding = !empty($headers['content-transfer-encoding']) ? $headers['content-transfer-encoding'] : null;
						// Single Part ?
						if(strtok($message->contentType, ';') == 'text/plain') {
							if($contentTransferEncoding == 'quoted-printable') {
								$content = trim(quoted_printable_decode($message->getContent()));
							} elseif($contentTransferEncoding == 'base64')  {
								// There is a file or an image inside the cotnent
								$content = base64_decode($message->getContent());
							} else {
								$content = $message->getContent();
							}
							$charset = self::parseCharset($message->contentType);
							if($charset != 'utf-8') {
								$content = utf8_encode($content);
							}
							$data['content'] = $content;
						}
					}
					$newMessages[] = $data;
					// Move the message to a virtual directory
					if($this->isInSyncMode() && $this->isInGmailMode()) {
				   		try {
				   			if(!$box->isFolder(_APPLICATION_EMAIL_FOLDER_)) {
				   				$mbox->createFolder(_APPLICATION_EMAIL_FOLDER_);
				   			}
							$mbox->moveMessage($i,_APPLICATION_EMAIL_FOLDER_);
						} catch(\Zend_Mail_Storage_Exception $e) {
							// Unable to move or recover old folder
						}
					}
					if ($i && ($i % 5 == 0)) {
						$box->handle()->noop(); // keep alive
					}
				}
			} catch(\Exception $e) {
				return $this->log($now,
									self::STATUS_ERROR,
									Msg(\Orx\Mailer\Messages::BUNDLE,\Orx\Mailer\Messages::ERROR_CONNECTION_MAILBOX_FAILDED),
									print_r($e,true)
									);
			}

			try {
				$nbOfNewMessages = 0;
				// Save All Message in Database
				foreach($newMessages as $messageData) {
					if(!\Orx\Mailer\MailBox\Message::isUnique($messageData['externalid'],$this->idusr,$this->boxid)) {
						continue;
					}
					$message = new \Orx\Mailer\MailBox\Message();
					
					// Check Message Unicity ?
					$messageData['idusr'] = $this->idusr;
					$messageData['boxid'] = $this->boxid;
					$messageData['status'] = self::STATUS_NEW;
					// Analyse Message Content to find parentid (message sent from my app)
					if(empty($messageData['content_html']) && !empty($messageData['content'])) {
						$messageData['content_html'] = $messageData['content'];
					} 
					if(!empty($messageData['content_html'])) {
						$messageData['parentid'] = \Orx\Mailer\MailBox\Message::parseContentForMessageId($messageData['content_html']);
						// We need to check if we can automatically link it to the contact
						if(!empty($messageData['parentid'])) {
							try {
								$parentMessage = \Fox\Dao::load(\Orx\Mailer\MailBox\Message::className(), $messageData['parentid']);
								foreach($parentMessage->receivers() as $receiver) {
									if($messageData['email'] == $receiver->email) {
										$messageData['reference'] = $receiver->reference;
										$messageData['reference_class_id'] = $receiver->reference_class_id;
										break;
									}
								}
							} catch(\Fox\Object\Exception\NotExist $e) {
								
							}
						}
					}
					$message->setFields($messageData);
					// Add Attachements
					if(!empty($messageData['attachements'])) {
						foreach($messageData['attachements'] as $attachementData) {
							$attachement = new \Orx\Mailer\MailBox\Message\Attachement();
							$attachementData['messageid'] = $message->getId();
							$attachement->setFields($attachementData);
							$attachement->physicalname = $attachement->cleanFileName();
							// Generate File on the server
							if($attachement->authorizedExtension()) {
								$attachement->generateFile();
							}
							$message->addToAttachements($attachement);
						}
					}
					// Add Receivers
					if(!empty($messageData['to'])) {
						foreach($messageData['to'] as $receiverData) {
							$receiver = new \Orx\Mailer\MailBox\Message\Receiver();
							$receiverData['messageid'] = $message->getId();
							$receiver->setFields($receiverData);
							$message->addToReceivers($receiver);
						}
					}
					
					// Add Cc's
					if(!empty($messageData['cc'])) {
						foreach($messageData['cc'] as $carboncopyData) {
							$carboncopy = new \Orx\Mailer\MailBox\Message\CarbonCopy();
							$carboncopyData['private'] = 0;
							$carboncopyData['messageid'] = $message->getId();
							$carboncopy->setFields($carboncopyData);
							$message->addToCarbonCopies($carboncopy);
						}
					}
					$nbOfNewMessages++;
					$message->create();
					if(!empty($messageData['reference'])) {
						//@TODO - Index Message in Search Engine
						
						// Add attachements to the contact
					}
					
				}
				return $this->log($now,
									self::STATUS_SUCCESS,
									Msg(\Orx\Mailer\Messages::BUNDLE,\Orx\Mailer\Messages::MAILBOX_SYNCHRONIZED),
									null,
									$nbOfNewMessages);
			} catch(\Fox\Object\Exception\Save $e) {
				return $this->log($now,self::STATUS_ERROR,print_r($e,true));
			}
		}
		return null;
	}
	
	/**
	 * Create a new entrie in the message logger system
	 * 
	 * @param string $status	(Error or Success)
	 * @param string $message	User friendly message
	 * @param string $technical_message	Technical Message Serialize
	 * @param int 	 $nbOfNewMessages
	 * 
	 * @return Synchronization
	 */
	public function log($sync_date,$status,$message,$technical_message = null,$nbOfNewMessages = 0) {
		if(is_object($message)) {
			$technical_message = serialize($message);
			$message = $e->getMessage();
		}
		try {
			$synchronization = new \Orx\Mailer\MailBox\Message\Synchronization();
			$synchronization->setFields(array('idusr'=> $this->idusr,
										  'boxid' => $this->boxid,
										  'status' => $status,
										  'sync_date' => self::sqlDateTime($sync_date),
										  'message' => $message,
										  'sys_message' => $technical_message,
										  'message_count' => $nbOfNewMessages
					));
			$synchronization->create();
			return $synchronization;
		} catch(\Fox\Object\Exception\Save $e) {
			// Should never occurs
			return false;
		}
	}
	
	/**
	 * Find and retrieve box handler
	 * @return \Orx\Mailer\MailBox
	 */
	public function box() {
		switch($this->protocol_to_retrieve) {
			case 'imap':
				return $this->imap();
			case 'pop3' :
				return $this->pop3();
		}
	}
	/**
	 * Label for CRUD
	 * @return string
	 */
	public function label() {
		return $this->email;
	}
	
	/**
	 * Find Inbox Messages
	 * @return array
	 */
	public function inboxMessagesSummary($limit = 100,$offset = 0) {
		
		$query = \Fox\Dao::infos(\Orx\Mailer\MailBox\Message::className())
			->handle()->createQueryBuilder();
		$query->select('m.messageid','m.status','m.received_date','m.email','m.name','m.reference','m.reference_class_id','m.subject')
			->from('messages','m')
			->leftJoin('m','message_attachements','a','m.messageid = a.messageid')
			->setMaxResults($limit)
			->orderBy('received_date','DESC');
		$fragment = new \Fox\Dao\SqlFragment('idusr = ? AND boxid = ? AND status <= ?',
						array($this->idusr,$this->boxid,2));
		return $fragment->pushIn($query)->execute()->fetchAll(\PDO::FETCH_ASSOC);
	}
	
	/**
	 * Check if the mailbox is in sync mode,meaning all change from
	 * the application should be replicate in the email server 
	 * return @bool
	 */
	public function isInSyncMode() {
		$keepacopy = $this->keepacopy;
		if($keepacopy) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * Check if the mailbox is using gmail has a cockpit
	 * 
	 * return @bool
	 */
	public function isInGmailMode() {
		$gmail_mode = $this->gmail_mode;
		if($gmail_mode) {
			return true;
		} else {
			return false;
		}
	}
}
\Fox\Dao::infos(MailBox::className(),new \Fox\Object\Info('user_mailboxes', \Fox\Databases::DB_APP))
	->addAutoIncrementField('boxid');
	
\Fox\Object\Links::add(\Fox\Dao::link(\Orx\Mailer\MailBox\Message\Synchronization::className(),\Orx\Mailer\MailBox\Message\Synchronization::LINK_BOX)
->to(MailBox::className(), MailBox::LINK_LAST_SYNCHRONIZATION)
->on('idusr','boxid')
->orderBy('sync_date','DESC')
->oneToOne()
);
// Sharing Users
\Fox\Object\Links::add(\Fox\Dao::link(\Orx\User\Shared\MailBox\Item::className(),\Orx\User\Shared\MailBox\Item::LINK_MAILBOX)
	->to(MailBox::className(), MailBox::LINK_SHARED_USERS)
	->on('idusr','boxid')
	->where('shared_reference = ?',array(\Orx\User\Shared\MailBox\Item::USER_REFERENCE))
	->versionFrom()
);
// Sharing Groups
\Fox\Object\Links::add(\Fox\Dao::link(\Orx\User\Shared\MailBox\Item::className(),\Orx\User\Shared\MailBox\Item::LINK_MAILBOX)
	->to(MailBox::className(), MailBox::LINK_SHARED_GROUPS)
	->on('idusr','boxid')
	->where('shared_reference = ?',array(\Orx\User\Shared\MailBox\Item::USER_GROUP_REFERENCE))
	->versionFrom()
);
// Sharing Items
\Fox\Object\Links::add(\Fox\Dao::link(\Orx\User\Shared\MailBox\Item::className(),\Orx\User\Shared\MailBox\Item::LINK_MAILBOX)
	->to(MailBox::className(), MailBox::LINK_SHARED_ITEMS)
	->on('idusr','boxid')
	->versionFrom()
);